package com.espirit.eap.util;

import java.io.File;
import java.math.BigInteger;
import java.security.KeyFactory;
import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import javax.crypto.Cipher;
import javax.servlet.ServletContext;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.json.JSONObject;

import com.espirit.eap.BaseActionSupportAction;
import com.espirit.eap.manager.SystemData;
import com.espirit.eap.manager.SystemDataService;
import com.espirit.eap.sa.LoginUserService;
import com.espirit.eap.sa.Website;
import com.espirit.eap.sa.WebsiteService;
import com.googlecode.cswish.Config;
import com.googlecode.cswish.security.UserTracerService;
import com.googlecode.cswish.struts.ContextHolder;
import com.googlecode.cswish.struts.interceptor.ExceptionMappingInterceptor;

// NOTICE: Run LicenseEncryptTest.testGenerateEncryptClass if the plug-in modified
// pom.xml calls LicenseEncryptTest.main to generate the plugin file
public class EapStartupManagerPlugin {

	private static final Logger logger = Logger.getLogger(EapStartupManagerPlugin.class);
	
	private CoreStartupManager eapStartupManager;

	private UserTracerService userTracerService;
	
    private Config config;
    
    private LoginUserService userService;
    
    private SystemDataService systemDataService;
    
    private WebsiteService websiteService;
    
    // See CoreStartupManager.start
    public void execute(
    		CoreStartupManager eapStartupManager,
    		UserTracerService userTracerService, 
    		Config config,
    		LoginUserService userService,
    		SystemDataService systemDataService,
    		WebsiteService websiteService,
    		
    		ServletContext servletContext) {
    	this.eapStartupManager = eapStartupManager;
    	this.userTracerService = userTracerService;
    	this.config = config;
    	this.userService = userService;
    	this.systemDataService = systemDataService;
    	this.websiteService = websiteService;
    	
    	startup(servletContext);
    }
    
	private void startup(ServletContext servletContext) {
		updateLicenseInfo();
	}
	
	private Map<String, String> licenseInfo;
	
	private void updateLicenseInfo() {
		boolean pass = true;
		try {
			if (licenseInfo == null) {
				String licenseFile = config.getRealWebRootPath() + "/license.data";
				String licenseData = FileUtils.readFileToString(new File(licenseFile), "UTF-8");
				String decryptData = decrypt(licenseData);
				JSONObject json = new JSONObject(decryptData);
				
				licenseInfo = new HashMap<String, String>();
				Iterator iter = json.keys();
				while (iter.hasNext()) {
					String key = (String)iter.next();
					String value = (String)json.get(key);
					licenseInfo.put(key, value);
				}
			}
		
			// login count
			int maxVisitCount = getInt(licenseInfo.get("maxVisitCount"));
			if (maxVisitCount != 0) {
				BaseActionSupportAction.maxEnabledVisitCount = maxVisitCount;
			}
			 
			// visit user at same time
			int maxUserCount = getInt(licenseInfo.get("maxUserCount"));
			if (maxUserCount != 0) {
				userTracerService.setMaxEnabledUserCount(maxUserCount);
			}
			
			// login user count
			int maxLoginCount = getInt(licenseInfo.get("maxLoginCount"));
			if (maxLoginCount != 0) {
				userService.maxEnabledLoginCount = maxLoginCount;
			}
			
			// expired time
			long expiredTime = getLong(licenseInfo.get("expiredTime"));
			if (expiredTime != 0) {				
				eapStartupManager.setExpiredTime(expiredTime);
			}
			
			// site count
			int maxSiteCount = getInt(licenseInfo.get("maxSiteCount"));
			if (maxSiteCount != 0) {
				websiteService.setMaxSiteCount(maxSiteCount);
			}
			
			// company information
			//TODO
			String companyName = licenseInfo.get("companyName");
			if (companyName != null) {
				// store the site name to SAAS DB
				ContextHolder.get().setSiteName(Website.DEFAULT_NAME);
				systemDataService.setValue(SystemData.COMPANY_NAME, companyName);
			}
		} catch (Throwable t) {
			// logger.error("license", t);
			// don't show debug information to avoid hacker trace the information and crack it
			pass = false;
		}
		
		if (!pass) {
			ExceptionMappingInterceptor.interceptorEx = "error.licenseExpired";
		}
	}

	private int getInt(String str) {
		if (str == null) {
			return 1;
		}
		
		return Integer.parseInt(str);
	}
	private long getLong(String str) {
		if (str == null) {
			return 1;
		}
		
		return Long.parseLong(str);
	}
	
	///////////////////////////////////////////////////////////////////////////////////////////////////////////////////
	/////////////////////////////////// move class LicenseDecrypt here for security reason ////////////////////////////
	// mixed text --> split it --> decrypt --> check MD5 
	public String decrypt(String text) {
		try {
			// NOTICE: must match with LicenseEncrypt.pubKey
			String priKey = "MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALzMlxo+M3DTWAE/oykyfioB7WVgiU/ELfqroUbb+Mh8REToHCPH2V2/6/LWERm6TL2U/RTeg1AM6eh8xCW6BDbulvZ7vh9HQUG9F9wWybVNI0OBKoY53bC7D8QaIn6YtgAIVfCZIvvyJRluiYcnTQhU40mqzx9b9KhLND2MTidFAgMBAAECgYAxAV3bRvftOj5mnJEKWXvcBYIMRWF+3eodXb1P+jr6Nx81T5Y2R2KJbg6diqkHp47RysFiUFt/RMrWOMRlEoKTGNN0IDTNVOcX/iDFDmVTvUUmXMcPHg9ENam3opn5mCMrWLYMU2o8JqGxZvFeJNrxR842exx5DMrfqBFUkFdKpQJBAPS9sGg3KqUH/KrbGhn41MBbcPFpV2VO9/Ij8nZBpWIvtLz/7WigLaShLOGFWfBRGVCygHyJkUNeYmeYx0IA7+sCQQDFfBNSIehu7hYN94tTqI8BRvZ0Z3JiRNtaiYdiFu6UvESVjVjupdELMkRl2NV9hJPK6ATrtdV4SQjKalE6sqmPAkAt4m5za3xcSn4Njlx7aBKDKeGn8ScIgGCuDh1EHJSkLuuEz7qz9ev4xJsEgRrWmm3TmlPKhYQtjEGIyLT64/AXAkBKWRDpDez0vlu9PGHOyBm2kGG+pydwCZ5PcpFug1Wk11A2wXUmu3R885q+gSi2zY/ahwk7OES6uTw/AFG+eH7PAkEA7vAKbW7CjEh/0zLgKnPDuDnxfPufh+UwtzQR7rmpDQesHvxO29VG6/jMDyEHn8zTkLOp4a3im1SF6GEo/aJRfA==";
			
			int index = text.indexOf('\n');
			String encrypText = text.substring(0, index);
			String content = simpleDecrypt(text.substring(index + 1));
	
			// prepare enviroment
			KeyFactory keyFactory = KeyFactory.getInstance("RSA");
			byte[] keyBytes = Base64.decodeBase64(priKey);
			PKCS8EncodedKeySpec priKeySpec = new PKCS8EncodedKeySpec(keyBytes);  
			PrivateKey privateKey = keyFactory.generatePrivate(priKeySpec);
			
            // decrypt it
            Cipher cipher = Cipher.getInstance("RSA");
            cipher.init(Cipher.DECRYPT_MODE, privateKey);
            
            byte[] encryptBytes = Base64.decodeBase64(encrypText);
			byte[] de = cipher.doFinal(encryptBytes);
			
			byte[] md5Bytes = MessageDigest.getInstance("MD5").digest(content.getBytes("UTF-8"));
			if (de.length == md5Bytes.length) {
				boolean equals = true;
				for (int i = 0; i < de.length; i++) {
					if (de[i] != md5Bytes[i]) {
						equals = false;
						break;
					}
				}
				if (equals) {
					return content;
				}
			}
		} catch (Exception e) {
			logger.error("license", e);
			// don't show debug information to avoid hacker trace the information and crack it
		}
		return null;
	}
	
	private String simpleDecrypt(String encrypted) {  
        if (encrypted == null)  
            return "";  
        if (encrypted.length() == 0)  
            return "";
  
        // NOTICE: it's same with LicenseEncrypt.simpleEncrypt
        String SEED = "0933912345463123732314329";
        int RADIX = 16;
        BigInteger bi_confuse = new BigInteger(SEED);
        try {  
            BigInteger bi_r1 = new BigInteger(encrypted, RADIX);  
            BigInteger bi_r0 = bi_r1.xor(bi_confuse);  
  
            return new String(bi_r0.toByteArray(), "UTF-8");  
        } catch (Exception e) {
            return "";  
        }  
    }
}
